package logic

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/apache/rocketmq-client-go/v2/consumer"
	"github.com/apache/rocketmq-client-go/v2/primitive"
	"github.com/go-redsync/redsync/v4"
	"github.com/go-redsync/redsync/v4/redis/goredis/v9"
	goredislib "github.com/redis/go-redis/v9"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"inventory_srv/global"
	"inventory_srv/inventory_proto"
	"inventory_srv/model"
	"time"
)

func (s *Server) SetInv(ctx context.Context, in *inventory_proto.GoodsInvInfo) (*inventory_proto.Empty, error) {
	var inv model.Inventory
	global.DB.Where(&model.Inventory{GoodsID: in.GoodsID}).Find(&inv)
	inv.GoodsID = in.GoodsID
	inv.Stocks = in.Nums
	tx := global.DB.Save(&inv)
	if tx.Error != nil {
		return nil, status.Errorf(codes.Internal, "添加库存失败")
	}
	return &inventory_proto.Empty{}, nil
}

func (s *Server) InvDetail(ctx context.Context, in *inventory_proto.GoodsInvInfo) (*inventory_proto.GoodsInvInfo, error) {
	var inv model.Inventory
	if result := global.DB.Where(&model.Inventory{GoodsID: in.GoodsID}).First(&inv); result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "没有该商品库存信息")
	}
	return &inventory_proto.GoodsInvInfo{
		GoodsID: inv.GoodsID,
		Nums:    inv.Stocks,
	}, nil
}

// 悲观锁 解决超卖
//func (s *Server) Sell(ctx context.Context, in *inventory_proto.SellInfo) (*inventory_proto.Empty, error) {
//	tx := global.DB.Begin()
//	for _, v := range in.GoodsInfo {
//		var inv model.Inventory
//		tx.Where(&model.Inventory{GoodsID: v.GoodsID}).Find(&inv)
//		if inv.Stocks < v.Nums {
//			tx.Rollback()
//			return nil, status.Errorf(codes.Internal, "库存不足")
//		}
//		inv.Stocks -= v.Nums
//		if err := tx.Save(&inv).Error; err != nil {
//			tx.Rollback()
//			return nil, status.Errorf(codes.Internal, "更新库存失败: %v", err)
//		}
//	}
//	if err := tx.Commit().Error; err != nil {
//		return nil, status.Errorf(codes.Internal, "提交事务失败: %v", err)
//	}
//	return &inventory_proto.Empty{}, nil
//}

// 乐观锁 解决超卖
//
//	func (s *Server) Sell(ctx context.Context, in *inventory_proto.SellInfo) (*inventory_proto.Empty, error) {
//		for _, v := range in.GoodsInfo {
//			for true {
//				var inv model.Inventory
//				global.DB.Where(&model.Inventory{GoodsID: v.GoodsID}).Find(&inv)
//				if inv.Stocks < v.Nums {
//					return nil, status.Errorf(codes.Internal, "库存不足")
//				}
//				tx := global.DB.Select("stocks", "version").Where("goods_id = ?", v.GoodsID).Where("version = ?", inv.Version).Updates(model.Inventory{Stocks: inv.Stocks - 1, Version: inv.Version + 1})
//				if tx.RowsAffected == 1 {
//					break
//				}
//			}
//		}
//		return &inventory_proto.Empty{}, nil
//	}
//

// redis 分布式锁 最小粒度
//func (s *Server) Sell(ctx context.Context, in *inventory_proto.SellInfo) (*inventory_proto.Empty, error) {
//	//连接redis
//	client := goredislib.NewClient(&goredislib.Options{
//		Addr: "127.0.0.1:6379",
//	})
//	pool := goredis.NewPool(client)
//	rs := redsync.New(pool)
//	//开启事务
//	tx := global.DB.Begin()
//	for _, v := range in.GoodsInfo {
//		mutexName := fmt.Sprintf("inventory-mutex")
//		mutex := rs.NewMutex(mutexName, redsync.WithExpiry(10*time.Second), redsync.WithTries(5))
//		//尝试连接
//		if err := mutex.LockContext(ctx); err != nil {
//			return nil, status.Errorf(codes.Internal, "获取Redis锁失败: %v", err)
//		}
//		//开启事务
//		tx := global.DB.Begin()
//		var inv model.Inventory
//		//查询数据库
//		result := tx.Where(&model.Inventory{GoodsID: v.GoodsID}).First(&inv)
//		if result.RowsAffected == 0 {
//			tx.Rollback()
//			return nil, status.Errorf(codes.NotFound, "没有该商品库存信息: %d", v.GoodsID)
//		}
//		if inv.Stocks < v.Nums {
//			tx.Rollback()
//			return nil, status.Errorf(codes.Internal, "库存不足")
//		}
//		//扣减库存
//		inv.Stocks -= v.Nums
//		if err := tx.Save(&inv).Error; err != nil {
//			tx.Rollback()
//			return nil, status.Errorf(codes.Internal, "更新库存失败: %v", err)
//		}
//		//提交事务
//		tx.Commit()
//		//事务完成后解锁
//		if ok, err := mutex.UnlockContext(ctx); !ok || err != nil {
//			return nil, status.Errorf(codes.Internal, "释放Redis锁失败: %v", err)
//		}
//	}
//	return &inventory_proto.Empty{}, nil
//}

// redis分布式锁 解决超卖
func (s *Server) Sell(ctx context.Context, in *inventory_proto.SellInfo) (*inventory_proto.Empty, error) {
	//连接redis
	client := goredislib.NewClient(&goredislib.Options{
		Addr: "127.0.0.1:6379",
	})
	pool := goredis.NewPool(client)
	rs := redsync.New(pool)

	mutexName := fmt.Sprintf("inventory-mutex")
	mutex := rs.NewMutex(mutexName, redsync.WithExpiry(10*time.Second), redsync.WithTries(5))
	//尝试连接
	if err := mutex.LockContext(ctx); err != nil {
		return nil, status.Errorf(codes.Internal, "获取Redis锁失败: %v", err)
	}
	//开启事务
	tx := global.DB.Begin()

	orderDeductionInventory := []model.OrderDeductionInventory{}
	for _, v := range in.GoodsInfo {
		var inv model.Inventory
		//查询数据库
		result := tx.Where(&model.Inventory{GoodsID: v.GoodsID}).First(&inv)
		if result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Errorf(codes.NotFound, "没有该商品库存信息: %d", v.GoodsID)
		}
		if inv.Stocks < v.Nums {
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "库存不足")
		}
		//扣减库存
		inv.Stocks -= v.Nums
		if err := tx.Save(&inv).Error; err != nil {
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "更新库存失败: %v", err)
		}
		//组装
		orderDeductionInventory = append(orderDeductionInventory, model.OrderDeductionInventory{
			OrderSn: in.OrderSn,
			GoodsID: v.GoodsID,
			Stocks:  v.Nums,
			Status:  1,
		})
	}
	//执行订单库存扣减记录的批量添加
	batches := tx.Model(&model.OrderDeductionInventory{}).CreateInBatches(orderDeductionInventory, 100)
	if batches.Error != nil {
		tx.Rollback()
		return nil, status.Errorf(codes.Internal, "批量添加订单库存扣减记录失败: %v", batches.Error)
	}
	//提交事务
	tx.Commit()
	//事务完成后解锁
	if ok, err := mutex.UnlockContext(ctx); !ok || err != nil {
		return nil, status.Errorf(codes.Internal, "释放Redis锁失败: %v", err)
	}
	return &inventory_proto.Empty{}, nil
}

// 尝试扣减库存
func (s *Server) TrySell(ctx context.Context, in *inventory_proto.SellInfo) (*inventory_proto.Empty, error) {
	//连接redis
	client := goredislib.NewClient(&goredislib.Options{
		Addr: "127.0.0.1:6379",
	})
	pool := goredis.NewPool(client)
	rs := redsync.New(pool)

	mutexName := fmt.Sprintf("inventory-mutex")
	mutex := rs.NewMutex(mutexName, redsync.WithExpiry(10*time.Second), redsync.WithTries(5))
	//尝试连接
	if err := mutex.LockContext(ctx); err != nil {
		return nil, status.Errorf(codes.Internal, "获取Redis锁失败: %v", err)
	}
	//开启事务
	tx := global.DB.Begin()
	for _, v := range in.GoodsInfo {
		var inv model.Inventory
		//查询数据库
		result := tx.Where(&model.Inventory{GoodsID: v.GoodsID}).First(&inv)
		if result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Errorf(codes.NotFound, "没有该商品库存信息: %d", v.GoodsID)
		}
		if inv.Stocks < v.Nums {
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "库存不足")
		}
		//修改冻结库存
		inv.Freeze += v.Nums
		if err := tx.Save(&inv).Error; err != nil {
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "更新库存失败: %v", err)
		}
	}
	//提交事务
	tx.Commit()
	//事务完成后解锁
	if ok, err := mutex.UnlockContext(ctx); !ok || err != nil {
		return nil, status.Errorf(codes.Internal, "释放Redis锁失败: %v", err)
	}
	return &inventory_proto.Empty{}, nil
}

// 确认扣减库存
func (s *Server) ConfirmSell(ctx context.Context, in *inventory_proto.SellInfo) (*inventory_proto.Empty, error) {
	//连接redis
	client := goredislib.NewClient(&goredislib.Options{
		Addr: "127.0.0.1:6379",
	})
	pool := goredis.NewPool(client)
	rs := redsync.New(pool)

	mutexName := fmt.Sprintf("inventory-mutex")
	mutex := rs.NewMutex(mutexName, redsync.WithExpiry(10*time.Second), redsync.WithTries(5))
	//尝试连接
	if err := mutex.LockContext(ctx); err != nil {
		return nil, status.Errorf(codes.Internal, "获取Redis锁失败: %v", err)
	}
	//开启事务
	tx := global.DB.Begin()
	for _, v := range in.GoodsInfo {
		var inv model.Inventory
		//查询数据库
		result := tx.Where(&model.Inventory{GoodsID: v.GoodsID}).First(&inv)
		if result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Errorf(codes.NotFound, "没有该商品库存信息: %d", v.GoodsID)
		}
		if inv.Stocks < v.Nums {
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "库存不足")
		}
		//扣减库存
		inv.Stocks -= v.Nums
		inv.Freeze -= v.Nums
		if err := tx.Save(&inv).Error; err != nil {
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "更新库存失败: %v", err)
		}
	}
	//提交事务
	tx.Commit()
	//事务完成后解锁
	if ok, err := mutex.UnlockContext(ctx); !ok || err != nil {
		return nil, status.Errorf(codes.Internal, "释放Redis锁失败: %v", err)
	}
	return &inventory_proto.Empty{}, nil
}

// 取消扣减库存
func (s *Server) CancelSell(ctx context.Context, in *inventory_proto.SellInfo) (*inventory_proto.Empty, error) {
	//连接redis
	client := goredislib.NewClient(&goredislib.Options{
		Addr: "127.0.0.1:6379",
	})
	pool := goredis.NewPool(client)
	rs := redsync.New(pool)

	mutexName := fmt.Sprintf("inventory-mutex")
	mutex := rs.NewMutex(mutexName, redsync.WithExpiry(10*time.Second), redsync.WithTries(5))
	//尝试连接
	if err := mutex.LockContext(ctx); err != nil {
		return nil, status.Errorf(codes.Internal, "获取Redis锁失败: %v", err)
	}
	//开启事务
	tx := global.DB.Begin()
	for _, v := range in.GoodsInfo {
		var inv model.Inventory
		//查询数据库
		result := tx.Where(&model.Inventory{GoodsID: v.GoodsID}).First(&inv)
		if result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Errorf(codes.NotFound, "没有该商品库存信息: %d", v.GoodsID)
		}
		if inv.Stocks < v.Nums {
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "库存不足")
		}
		//取消扣减库存
		inv.Freeze -= v.Nums
		if err := tx.Save(&inv).Error; err != nil {
			tx.Rollback()
			return nil, status.Errorf(codes.Internal, "更新库存失败: %v", err)
		}
	}
	//提交事务
	tx.Commit()
	//事务完成后解锁
	if ok, err := mutex.UnlockContext(ctx); !ok || err != nil {
		return nil, status.Errorf(codes.Internal, "释放Redis锁失败: %v", err)
	}
	return &inventory_proto.Empty{}, nil
}

//func (s *Server) ReBack(ctx context.Context, in *inventory_proto.SellInfo) (*inventory_proto.Empty, error) {
//	orderDeductionInventory := []model.OrderDeductionInventory{}
//	tx := global.DB.Where(&model.OrderDeductionInventory{OrderSn: in.OrderSn, Status: 1}).Find(&orderDeductionInventory)
//	if tx.Error != nil {
//		return nil, status.Errorf(codes.NotFound, "查询订单库存扣减记录信息失败")
//	}
//	res := global.DB.Begin()
//	for _, v := range orderDeductionInventory {
//		var inventory model.Inventory
//		first := res.Where(&model.Inventory{GoodsID: v.GoodsID}).First(&inventory)
//		if first.RowsAffected == 0 {
//			res.Rollback()
//			return nil, status.Errorf(codes.NotFound, "没有该商品库存信息")
//		}
//		inventory.Stocks += v.Stocks
//		save := res.Save(&inventory)
//		if save.Error != nil {
//			res.Rollback()
//			return nil, status.Errorf(codes.NotFound, "归还库存失败")
//		}
//	}
//	update := res.Where(&model.OrderDeductionInventory{OrderSn: in.OrderSn}).Update("status", 2)
//	if update.Error != nil {
//		res.Rollback()
//		return nil, status.Errorf(codes.NotFound, "修改订单扣减库存信息状态失败")
//	}
//	return &inventory_proto.Empty{}, nil
//}

func (s *Server) ReBack(ctx context.Context, in *inventory_proto.SellInfo) (*inventory_proto.Empty, error) {
	var orderDeductionInventory []model.OrderDeductionInventory
	tx := global.DB.Where(&model.OrderDeductionInventory{OrderSn: in.OrderSn, Status: 1}).Find(&orderDeductionInventory)
	if tx.Error != nil {
		return nil, status.Errorf(codes.Internal, "查询订单库存扣减信息失败")
	}
	res := global.DB.Begin()
	for _, v := range orderDeductionInventory {
		inv := model.Inventory{}
		first := res.Where(&model.Inventory{GoodsID: v.GoodsID}).First(&inv)
		if first.RowsAffected == 0 {
			res.Rollback()
			return nil, status.Errorf(codes.NotFound, "没有该商品信息")
		}
		inv.Stocks += v.Stocks
		save := res.Save(&inv)
		if save.Error != nil {
			res.Rollback()
			return nil, status.Errorf(codes.Internal, "归还库存失败")
		}
	}
	update := res.Where(&model.OrderDeductionInventory{OrderSn: in.OrderSn}).Update("status", 2)
	if update.Error != nil {
		res.Rollback()
		return nil, status.Errorf(codes.Internal, "修改订单库存扣减信息状态失败")
	}
	res.Commit()
	return &inventory_proto.Empty{}, nil
}

func AutoReback(ctx context.Context, msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
	type OrderInfo struct {
		OrderSn string
	}
	for i := range msgs {
		var orderInfo OrderInfo
		json.Unmarshal(msgs[i].Body, &orderInfo)
		var orderDeductionInventory []model.OrderDeductionInventory
		tx := global.DB.Where(&model.OrderDeductionInventory{OrderSn: orderInfo.OrderSn, Status: 1}).Find(&orderDeductionInventory)
		if tx.Error != nil {
			zap.S().Info("查询订单库存扣减信息失败")
			return consumer.ConsumeRetryLater, nil
		}
		res := global.DB.Begin()
		for _, v := range orderDeductionInventory {
			inv := model.Inventory{}
			first := res.Where(&model.Inventory{GoodsID: v.GoodsID}).First(&inv)
			if first.RowsAffected == 0 {
				res.Rollback()
				zap.S().Info("没有该商品信息")
				return consumer.ConsumeRetryLater, nil
			}
			inv.Stocks += v.Stocks
			save := res.Save(&inv)
			if save.Error != nil {
				res.Rollback()
				zap.S().Info("归还库存失败")
				return consumer.ConsumeRetryLater, nil
			}
		}
		update := res.Where(&model.OrderDeductionInventory{OrderSn: orderInfo.OrderSn}).Update("status", 2)
		if update.Error != nil {
			res.Rollback()
			zap.S().Info("修改订单库存扣减信息状态失败")
			return consumer.ConsumeRetryLater, nil
		}
		res.Commit()
	}
	return consumer.ConsumeSuccess, nil
}
